﻿using System;
using MugenInjection.Delegates;
using MugenInjection.Syntax;

namespace MugenInjection.Bindings.Builders
{
    internal class ConditionBuilder<T> : IConditionBuilder<T>
    {
        #region Fields

        private readonly BindingBuilderConfigurator _configurator;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="ConditionBuilder{T}" /> class.
        /// </summary>
        public ConditionBuilder()
        {
            _configurator = new BindingBuilderConfigurator();
        }

        #endregion

        #region Methods

        public CanResolveBindingDelegate Build()
        {
            Type type = typeof (T);
            CanResolveBindingDelegate bindingDelegate = _configurator.GetCanResolveCondition();
            return context => context.Service == type && bindingDelegate(context);
        }

        #endregion

        #region Implementation of IConditionBuilder<T>

        /// <summary>
        ///     Indicates that the condition should be registered with the specified name.
        /// </summary>
        /// <param name="name">The name to give the condition.</param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> Named(string name)
        {
            _configurator.SetKeyedBinding(name);
            return this;
        }

        /// <summary>
        /// Indicates that the condition should be registered with the specified key.
        /// </summary>
        /// <param name="key">The key to give the condition.</param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> Keyed(object key)
        {
            _configurator.SetKeyedBinding(key);
            return this;
        }

        /// <summary>
        ///     Indicates that the binding should be used only for requests that support the specified condition.
        /// </summary>
        /// <param name="canResolveBindingDelegate">
        ///     The specified <see cref="CanResolveBindingDelegate" />
        /// </param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> When(CanResolveBindingDelegate canResolveBindingDelegate)
        {
            _configurator.SetCanResolve(canResolveBindingDelegate);
            return this;
        }

        /// <summary>
        ///     Indicates that the binding should be used only for injections on the specified type.
        ///     Types that derive from the specified type will not be considered as valid target.
        /// </summary>
        /// <param name="type">The type of target.</param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenInto(Type type)
        {
            _configurator.SetWhenCanResolveType(type);
            return this;
        }

        /// <summary>
        ///     Indicates that the binding should be used only for injections on the specified type <see cref="TType" />.
        ///     Types that derive from the specified type will not be considered as valid target.
        /// </summary>
        /// <typeparam name="TType">The type of target.</typeparam>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenInto<TType>()
        {
            return WhenInto(typeof (TType));
        }

        /// <summary>
        ///     Indicates that the binding should be used only for injections on the specified type.
        ///     Types that derive from the specified type are considered as valid targets.
        /// </summary>
        /// <param name="type">The type of target.</param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenIntoIsAssignable(Type type)
        {
            _configurator.SetWhenCanResolveTypeAssignable(type);
            return this;
        }

        /// <summary>
        ///     Indicates that the binding should be used only for injections on the specified type <see cref="TType" />.
        ///     Types that derive from the specified type are considered as valid targets.
        /// </summary>
        /// <typeparam name="TType">The type of target.</typeparam>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenIntoIsAssignable<TType>()
        {
            return WhenIntoIsAssignable(typeof (TType));
        }

        /// <summary>
        ///     Indicates that the binding should be used only for injections on the specified type when namespace equals.
        /// </summary>
        /// <param name="name">The name of namespace.</param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenNamespaceEqual(string name)
        {
            _configurator.SetWhenCanResolveNamespace(name);
            return this;
        }

        /// <summary>
        ///     Indicates that the binding should be used only when the class being injected has
        ///     an attribute of the specified type.
        /// </summary>
        /// <param name="attributeType">The type of attribute.</param>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenClassHasAttribute(Type attributeType)
        {
            _configurator.SetWhenClassHasAttribute(attributeType);
            return this;
        }

        /// <summary>
        ///     Indicates that the binding should be used only when the class being injected has
        ///     an attribute of the specified type.
        /// </summary>
        /// <typeparam name="TAttribute">The type of attribute.</typeparam>
        /// <returns>The fluent syntax.</returns>
        public IConditionBuilder<T> WhenClassHasAttribute<TAttribute>() where TAttribute : Attribute
        {
            return WhenClassHasAttribute(typeof (TAttribute));
        }

        #endregion
    }
}